package cn.topcode.unicorn.utils;

import java.util.List;

/**
 * 字节处理工具
 * @author Unicorn Lien
 * 2016年10月19日 下午3:35:39 创建
 */
public class ByteUtil {

    /**
     * 字符串数字转BCD编码长度为奇数 需要补充4位 默认头部 + ‘0’
     */
    public static final byte BCD_FILL_MODE_HEAD = 1;
    /**
     * 字符串数字转BCD编码长度为奇数 需要补充4位 默认头部
     * 尾部为自定义填充模式
     */
    public static final byte BCD_FILL_MODE_TAIL = 2;

    private ByteUtil() {}

    public static byte[] long2byteArr(long num) {
        byte[] data = new byte[8];
        data[0] = (byte) (num >> 54);
        data[1] = (byte) (num >> 48);
        data[2] = (byte) (num >> 40);
        data[3] = (byte) (num >> 32);
        data[4] = (byte) (num >> 24);
        data[5] = (byte) (num >> 16);
        data[6] = (byte) (num >> 8);
        data[7] = (byte) num;
        return data;
    }

    /**
     * 将字节或字节数组转换为16进制字符串（小写）
     * @author Unicorn Lien
     * 2016年10月19日 下午3:37:36 创建
     * @param arr
     * @return
     */
    public static String byte2Hex(byte... arr) {
        if(arr == null) {
            return null;
        }
        //  用来将字节转换成 16 进制表示的字符
        char[] hexDigits = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'};
        // 每个字节用 16 进制表示的话，使用两个字符
        char[] str = new char[arr.length * 2];
        // 表示转换结果中对应的字符位置
        int k = 0; 
        // 从第一个字节开始，对每一个字节,转换成 16 进制字符的转换
        for (int i = 0; i < arr.length; i++) {
            // 取第 i 个字节
            byte byte0 = arr[i]; 
            // 取字节中高 4 位的数字转换, >>> 为逻辑右移，将符号位一起右移
            str[k++] = hexDigits[byte0 >>> 4 & 0xf]; 
            // 取字节中低 4 位的数字转换
            str[k++] = hexDigits[byte0 & 0xf];
        }
        // 换后的结果转换为字符串
        return new String(str); 
    }
    
    /**
     * 字节数组转整型数组
     * @author Unicorn Lien
     * 2016年11月12日 上午3:26:05 创建
     * @param byteArr   字节数组
     * @return  整型数组
     */
    public static int[] byteArray2IntArray(byte[] byteArr) {
        if(byteArr == null) {
            return null;
        }
        int[] intArr = new int[byteArr.length];
        for(int i = 0 ; i < intArr.length ; i ++ ) {
            intArr[i] = byteArr[i];
        }
        return intArr;
    }
    
    /**
     * List<Byte>转byte[]
     * @author Unicorn Lien
     * 2016年11月12日 上午3:26:29 创建
     * @param list  字节列表
     * @return  字节数组
     */
    public static byte[] byteList2ByteArray(List<Byte> list) {
        if(list != null) {
            byte[] arr = new byte[list.size()];
            for(int i = 0 ; i < arr.length ; i ++ ) {
                arr[i] = list.get(i).byteValue();
            }
            return arr;
        }
        return null;
    }

    /**
     * 十六进制字符串转换为字节数组
     * @param hex	十六进制字符串
     * @return	字节数组
     */
    public static byte[] hex2byte(String hex) {
        if (hex == null || hex.equals("")) {
            return null;
        }
        hex = hex.toUpperCase();
        int length = hex.length() / 2;
        char[] hexChars = hex.toCharArray();
        byte[] d = new byte[length];
        for (int i = 0; i < length; i++) {
            int pos = i * 2;
            d[i] = (byte) (charToByte(hexChars[pos]) << 4 | charToByte(hexChars[pos + 1]));
        }
        return d;
    }

    private static byte charToByte(char c) {
        return (byte) "0123456789ABCDEF".indexOf(c);
    }

    /**
     * 十六进制字符串转换为BCD编码字节数组
     * @param asc
     * @return
     */
    public static byte[] str2bcd(String asc) {
        return str2bcd(asc, BCD_FILL_MODE_HEAD, "0");
    }

    /**
     * 十六进制字符串转换为BCD编码字节数组
     * @param asc
     * @param fillMode  填充模式
     * @param code      填充码
     * @return
     * @see #BCD_FILL_MODE_HEAD
     * @see #BCD_FILL_MODE_TAIL
     */
    public static byte[] str2bcd(String asc, byte fillMode, String code) {
        int len = asc.length();
        int mod = len % 2;
        if (mod != 0) {
            if (BCD_FILL_MODE_HEAD == fillMode) {
                asc = checkCode(code) + asc;
            } else if (BCD_FILL_MODE_TAIL == fillMode) {
                asc += checkCode(code);
            }
            len = asc.length();
        }

        byte[] abt;
        if (len >= 2) {
            len >>= 1;
        }

        byte[] bbt = new byte[len];
        abt = asc.getBytes();

        for(int p = 0; p < asc.length() / 2; ++p) {
            int j;
            if (abt[2 * p] >= 48 && abt[2 * p] <= 57) {
                j = abt[2 * p] - 48;
            } else if (abt[2 * p] >= 97 && abt[2 * p] <= 122) {
                j = abt[2 * p] - 97 + 10;
            } else {
                j = abt[2 * p] - 65 + 10;
            }

            int k;
            if (abt[2 * p + 1] >= 48 && abt[2 * p + 1] <= 57) {
                k = abt[2 * p + 1] - 48;
            } else if (abt[2 * p + 1] >= 97 && abt[2 * p + 1] <= 122) {
                k = abt[2 * p + 1] - 97 + 10;
            } else {
                k = abt[2 * p + 1] - 65 + 10;
            }

            int a = (j << 4) + k;
            byte b = (byte)a;
            bbt[p] = b;
        }

        return bbt;
    }

    private static String checkCode(String code) {
        String c = code = code.trim();
        if (code.startsWith("0x") || code.startsWith("0X")) {
            c = code.substring(2);
        }
        if (c.isEmpty() || c.length() > 1) {
            throw new IllegalArgumentException("不正确的补码");
        }
        return c;
    }

    /**
     * ascii编码字节数组转bcd编码字节数组
     * @param ascii
     * @return
     */
    public static byte[] asc2bcd(byte[] ascii) {
        return asc2bcd(ascii, ascii.length);
    }

    /**
     * ascii编码字节数组转bcd编码字节数组
     * @param ascii
     * @param ascLength
     * @return
     */
    public static byte[] asc2bcd(byte[] ascii, int ascLength) {
        byte[] bcd = new byte[ascLength / 2];
        int j = 0;

        for(int i = 0; i < (ascLength + 1) / 2; ++i) {
            bcd[i] = asc2bcd(ascii[j++]);
            bcd[i] = (byte)((j >= ascLength ? 0 : asc2bcd(ascii[j++])) + (bcd[i] << 4));
        }

        return bcd;
    }

    /**
     * bcd编码字节数组转字符串
     * @param bytes
     * @return
     */
    public static String bcd2str(byte[] bytes) {
        char[] temp = new char[bytes.length * 2];

        for(int i = 0; i < bytes.length; ++i) {
            char val = (char)((bytes[i] & 240) >> 4 & 15);
            temp[i * 2] = (char)(val > '\t' ? val + 65 - 10 : val + 48);
            val = (char)(bytes[i] & 15);
            temp[i * 2 + 1] = (char)(val > '\t' ? val + 65 - 10 : val + 48);
        }

        return new String(temp);
    }

    private static byte asc2bcd(byte asc) {
        byte bcd;
        if (asc >= 48 && asc <= 57) {
            bcd = (byte)(asc - 48);
        } else if (asc >= 65 && asc <= 70) {
            bcd = (byte)(asc - 65 + 10);
        } else if (asc >= 97 && asc <= 102) {
            bcd = (byte)(asc - 97 + 10);
        } else {
            bcd = (byte)(asc - 48);
        }

        return bcd;
    }

    public static void main(String[] args) {
        String password = "CB08AACAA3D";
        System.out.println("str2bcd，尾部填充模式，补F----" + byte2Hex(str2bcd(password, BCD_FILL_MODE_TAIL, "0xF")));
        System.out.println("str2bcd，头部填充模式，补0(默认)----" + byte2Hex(str2bcd("1234567")));
    }
}
